<?php
/**
 * Created by PhpStorm
 * User: Jhinwy
 * Date: 8/8/24
 * Time: 11:32 AM
 */

namespace app\common\service\video;

use app\common\model\skits\SkitsEpisodes;
use think\facade\Log;
use think\File;

class VideoService
{
    const CLARITY_HD  = 1;
    const CLARITY_UHD = 2;


    protected static $dir = 'videos';
    protected static $ext = '.m3u8';

    // 视频清晰度
    protected static $hd  = '720x1280';
    protected static $uhd = '1080x1920';


    /**
     * 上传视频
     *
     * @param File $video
     * @param $skitsId
     * @param $episodesOrder
     * @return array
     */
    public static function uploadVideo(File $video, $skitsId, int $compress = 100, int $clarity = 1)
    {
        $size       = $video->getSize();
        $originName = $video->getOriginalName();
        $videoOrder = self::getVideoOrder($originName);
        if (!$videoOrder) {

            return ['err' => -1, 'msg' => '视频名称不符合规范，请标注视频为第几集'];
        }
        $videoPath = self::getVideoPath($skitsId, $videoOrder);
        $videoName = self::getVideoName($skitsId, $videoOrder);
        $tsName    = self::getTsName($skitsId, $videoOrder);
        $fullPath  = $videoPath . $videoName;
        $tsPath    = $videoPath . $tsName;
        $realPath  = self::saveVideo($video->getRealPath(), $skitsId, $videoOrder, $originName);
        if (!$realPath) {

            return ['err' => -4, 'msg' => '保存视频失败'];
        }

        // 是否压缩视频  大于100M时压缩
        if ($compress != 100 && self::convertSizeToMB($size) >= 150) {
            $videoBitrate = self::getVideoBitrate($size, $realPath);
            Log::write('压缩视频原大小：' . $videoBitrate);
            $compressPath = self::compressVideo($realPath, $videoBitrate, $skitsId, $videoOrder, $originName, $compress, $clarity);
            if (!self::mp4ToM3u8($compressPath, $fullPath, $tsPath)) {

                return ['err' => -2, 'msg' => '第' . $videoOrder . '集视频转m3u8流媒体文件失败'];
            }
            unlink($realPath);
            unlink($compressPath);
        } else {
            if (!self::mp4ToM3u8($realPath, $fullPath, $tsPath)) {

                return ['err' => -2, 'msg' => '第' . $videoOrder . '集视频转m3u8流媒体文件失败'];
            }
            unlink($realPath);
        }

        if (!self::uploadOssVideo($skitsId, $videoOrder, $videoPath)) {

            return ['err' => -3, 'msg' => '第' . $videoOrder . '集视频上传失败'];
        }
        $episodes = SkitsEpisodes::where('skits_id', $skitsId)->where('episodes_order', $videoOrder)->findOrEmpty();
        // 保存剧集
        if ($episodes->isEmpty()) {
            $episodes                 = new SkitsEpisodes();
            $episodes->skits_id       = $skitsId;
            $episodes->episodes_order = $videoOrder;
            $episodes->episodes_name  = self::getEpisodesName($videoOrder);
        }
        $episodes->episodes_path = $videoName;
        $episodes->status        = 1;
        if (!$episodes->save()) {

            return ['err' => -4, 'msg' => '第' . $videoOrder . '集视频保存失败'];
        }

        return ['err' => 0, 'msg' => '上传成功'];
    }


    public static function getSize($size)
    {
        return round($size / 1024, 2);
    }


    public static function convertSizeToMB($size)
    {
        return round($size / (1024 * 1024), 2);
    }


    /**
     * 保存视频
     *
     * @param $realPath
     * @param $videoName
     * @return bool|string
     */
    public static function saveVideo($realPath, $skitsId, $videoOrder, $videoName)
    {
        $videoPath = public_path('uploads/videos') . $skitsId . '/' . $videoOrder . '/' . $videoName;
        if (file_put_contents($videoPath, file_get_contents($realPath))) {

            return $videoPath;
        }

        return false;
    }


    /**
     * 获取短剧名称
     *
     * @param $order
     * @return string
     */
    public static function getEpisodesName($order)
    {
        return '第' . $order . '集';
    }


    /**
     * 获取剧集编号
     *
     * @param $videoName
     * @return bool|int
     */
    public static function getVideoOrder($videoName)
    {
        preg_match('/\d+/', $videoName, $matches);
        $order = intval($matches[0] ?? 0);
        if (!is_numeric($order)) {

            return false;
        }
        if ($order < 1) {

            return false;
        }

        return (int)$order;
    }


    /**
     * 上传视频到OSS
     *
     * @param $skitsId
     * @param $episodesOrder
     * @param $videoPath
     * @return bool
     */
    public static function uploadOssVideo($skitsId, $episodesOrder, $videoPath)
    {
        $files = self::getAllVideo($skitsId, $episodesOrder);

        $oss = new OssVideoService();
        foreach ($files as $file) {
            if ($oss->uploadVideo($skitsId, $episodesOrder, $videoPath . $file, $file)) {
                unlink($videoPath . $file);
            } else {

                return false;
            }
        }

        return true;
    }


    /**
     * 获取视频
     *
     * @param $size
     * @param $inputPath
     * @return int
     */
    public static function getVideoBitrate($size, $inputPath)
    {
        $command = '/usr/local/ffmpeg/bin/ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ' . $inputPath;
        exec($command, $output, $result);

        if (!empty($output)) {
            $duration = $output[0] ?? 0;
            $bitrate  = ($size * 8) / $duration;

            return (int)($bitrate / 1000);
        }

        return 100;
    }


    /**
     * 设置视频压缩
     *
     * @param $videoBitrate
     * @param $compress
     * @return int
     */
    public static function setVideoBitrate($videoBitrate, $compress)
    {
        return (int)($videoBitrate * ($compress / 100));
    }


    /**
     * MP4转流媒体文件
     *
     * @param $inputPath
     * @param $outputPath
     * @param $tsPath
     * @return bool
     */
    public static function mp4ToM3u8($inputPath, $outputPath, $tsPath)
    {
        if (!$inputPath || !$outputPath) {

            return false;
        }

        // 是否存在流媒体文件
        if (file_exists($outputPath)) {

            return true;
        }

        $command = '/usr/local/ffmpeg/bin/ffmpeg -i "' . $inputPath . '" -c:v copy -c:a copy -hls_time 10 -hls_list_size 0 -hls_segment_filename "' . $tsPath . '" "' . $outputPath . '"';

        system($command, $result);
        Log::write('视频转m3u8 ===>' . $command);
        Log::write('视频转m3u8 ===>' . json_encode($result));


        return $result === 0;
    }

    /**
     * 压缩视频
     *
     * @param $inputPath
     * @param $videoBitrate
     * @param $skitsId
     * @param $videoOrder
     * @param $videoName
     * @param int $compress
     * @param int $clarity
     * @return string
     */
    public static function compressVideo($inputPath, $videoBitrate, $skitsId, $videoOrder, $videoName, $compress = 100, $clarity = 1)
    {
        $bitrate = self::setVideoBitrate($videoBitrate, $compress);
        $command = '/usr/local/ffmpeg/bin/ffmpeg -i "' . $inputPath . '" -threads 10 -preset veryfast -b:v ' . $bitrate . 'k -r 24';
        // 清晰度转换
        switch ($clarity) {
            case self::CLARITY_HD :
                $command .= ' -s ' . self::$hd;
                break;
            case self::CLARITY_UHD :
                $command .= ' -s ' . self::$uhd;
                break;
        }
        $compressPath = public_path('uploads/videos') . $skitsId . '/' . $videoOrder . '/压缩_' . $videoName;
        $command      .= ' "' . $compressPath . '"';

        system($command, $result);
        Log::write('压缩视频 ===>' . $command);
        Log::write('压缩视频 ===>' . json_encode($result));

        return $result === 0 ? $compressPath : '';
    }


    /**
     * 获取所有视频文件
     *
     * @param $skitsId
     * @param $episodesOrder
     * @return array
     */
    public static function getAllVideo($skitsId, $episodesOrder)
    {
        // 使用 scandir 函数获取目录中的所有文件和子目录
        $files = scandir(self::getVideoPath($skitsId, $episodesOrder));
        // 删除数组中的 "." 和 ".." 目录
        return array_diff($files, ['.', '..']);
    }


    /**
     * 获取TS文件名称
     *
     * @param $skitsId
     * @param $episodesOrder
     * @return string
     */
    public static function getTsName($skitsId, $episodesOrder)
    {
        return md5($skitsId . '_' . $episodesOrder) . '_%03d.ts';
    }


    /**
     * 获取视频名称
     *
     * @param $skitsId
     * @param $episodesOrder
     * @return string
     */
    public static function getVideoName($skitsId, $episodesOrder)
    {
        return md5($skitsId . '_' . $episodesOrder) . self::$ext;
    }


    /**
     * 获取视频路径
     *
     * @param $skitsId
     * @param $episodesOrder
     * @return string
     */
    public static function getVideoPath($skitsId, $episodesOrder)
    {
        $videoPath = self::getPath() . $skitsId . '/' . $episodesOrder . '/';
        if (!is_dir($videoPath)) {
            mkdir($videoPath, 0755, true);
        }

        return $videoPath;
    }


    /**
     * 获取路径
     *
     * @return string
     */
    public static function getPath()
    {
        $path = public_path('uploads/' . self::$dir);
        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }

        return $path;
    }
}
